﻿


Namespace YahooManaged.Finance.Indicators


    Public Class RSI
        Implements ISingleValueIndicator

        Public ReadOnly Property Name() As String Implements IIndicator.Name
            Get
                Return "Relative Strength Index"
            End Get
        End Property

        Public ReadOnly Property IsRealative As Boolean Implements IIndicator.IsRealative
            Get
                Return True
            End Get
        End Property

        Public ReadOnly Property ScaleMaximum() As Double Implements IIndicator.ScaleMaximum
            Get
                Return 100
            End Get
        End Property

        Public ReadOnly Property ScaleMinimum() As Double Implements IIndicator.ScaleMinimum
            Get
                Return 0
            End Get
        End Property

        Public Property Period() As Integer = 14 Implements IIndicator.Period


        Private mEMA As New EMA

        Public Overridable Function Calculate(ByVal values As IEnumerable(Of KeyValuePair(Of Date, Double))) As Dictionary(Of Date, Double)() Implements ISingleValueIndicator.Calculate
            Dim rsiResult As New Dictionary(Of Date, Double)

            Dim quoteValues As New List(Of KeyValuePair(Of Date, Double))(values)
            quoteValues.Sort(New QuotesSorter)

            If quoteValues.Count > 0 Then
                rsiResult.Add(quoteValues(0).Key, 50)
                Dim up, down As Double
                Dim upCount, downCount As Integer
                Dim aveUp, aveDown As Double
                Dim avgUp As New List(Of KeyValuePair(Of Date, Double))
                Dim avgDown As New List(Of KeyValuePair(Of Date, Double))

                Dim rs As Double
                For i As Integer = 1 To quoteValues.Count - 1
                    Dim periodLength As Integer = Math.Min(i, Me.Period)

                    up = 0
                    down = 0
                    upCount = 0
                    downCount = 0
                    aveUp = 0
                    aveDown = 0
                    rs = 0
                    For s As Integer = i - periodLength + 1 To i
                        If quoteValues(s).Value > quoteValues(s - 1).Value Then
                            up += quoteValues(s).Value - quoteValues(s - 1).Value
                            upCount += 1
                        ElseIf quoteValues(s).Value < quoteValues(s - 1).Value Then
                            down += quoteValues(s - 1).Value - quoteValues(s).Value
                            downCount += 1
                        End If
                    Next

                    If upCount > 0 Then aveUp = up / upCount
                    If downCount > 0 Then aveDown = down / downCount

                    If aveDown <> 0 Then rs = aveUp / aveDown
                    rsiResult.Add(quoteValues(i).Key, 100 - (100 / (1 + aveUp / aveDown)))
                Next
            End If


            Return New Dictionary(Of Date, Double)() {rsiResult}
        End Function

        Public Overrides Function ToString() As String
            Return Me.Name & " " & Me.Period
        End Function

    End Class

End Namespace